package edu.tces482.plms;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
import jssc.SerialPortList;
import edu.tces482.plms.zigbee.ZBPacketProcessor;

public class PMSServer {

	private static final String EXPECTED_PORT_NAME = "/dev/tty.usbserial-A9014OBQ";
	
	private static final Logger LOGGER = Logger.getLogger(PMSServer.class.getName());
	
	private static SerialPort serialPort;
	private static ZBPacketProcessor dataProcessor = new ZBPacketProcessor();

	public static void main(String[] args) 
	{
		System.out.println("Starting PLMS server");
		
		Runtime.getRuntime().addShutdownHook(new GracefulShutdown());
		
		// Setup logging
		Logger globalLogger = LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME);
		globalLogger.setLevel(Level.ALL);
		for (Handler h : globalLogger.getHandlers()) 
		{
			h.setLevel(Level.ALL);
		}
		System.out.println("Default log level set to: " + globalLogger.getLevel());
		
		boolean portAvailable = false;

		while (!portAvailable)
		{
			String[] portNames = SerialPortList.getPortNames();
			for(int i = 0; i < portNames.length; i++)
			{
				LOGGER.fine("Available devices: " + portNames[i]);
				if (EXPECTED_PORT_NAME.equals(portNames[i]))
				{
					portAvailable = true;
					LOGGER.info("Found device: " + EXPECTED_PORT_NAME);
					break;
				}
			}
		}
		
		serialPort = new SerialPort(EXPECTED_PORT_NAME);
		try 
		{	
			serialPort.openPort();										//Open serial port
			serialPort.setParams(SerialPort.BAUDRATE_9600, 8, 1, 0);	//Set params.
            serialPort.setEventsMask(SerialPort.MASK_RXCHAR);			//Set mask
            serialPort.addEventListener(new SerialPortReader());		//Add SerialPortEventListener
		}
		catch (SerialPortException ex) 
		{
			LOGGER.severe(ex.toString());
		}
	}

	public static byte[] hexStringToByteArray(String s) {
		int len = s.length();
		byte[] data = new byte[len / 2];
		for (int i = 0; i < len; i += 2) {
			data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
					+ Character.digit(s.charAt(i+1), 16));
		}
		return data;
	}

	private PMSServer() {
		// Avoid instantiation.
	}
	
	/*
     * In this class must implement the method serialEvent, through it we learn about 
     * events that happened to our port. But we will not report on all events but only 
     * those that we put in the mask. In this case the arrival of the data and change the 
     * status lines CTS and DSR
     */
    private static class SerialPortReader implements SerialPortEventListener {
    	@Override
        public void serialEvent(SerialPortEvent event) 
    	{
            if(event.isRXCHAR())									//If data is available
            {
            	int bytesAvailable = event.getEventValue();
            	if (bytesAvailable > 20) {
            		try {
						dataProcessor.submit(serialPort.readBytes(bytesAvailable));
					} catch (SerialPortException e) {
						LOGGER.severe(e.toString());
					}
            	}
            }
        }
    }
    
    private static class GracefulShutdown extends Thread {
		@Override
		public void run() {
			if (serialPort != null && serialPort.isOpened()) {
				try {
					System.out.println("PLMS server shutting down");
					serialPort.closePort();
					
				} catch (SerialPortException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}
